/******************************************************************************
 * @file     main.c
 * @brief    Demonstrate how to implement a USB dual virtual com port device.
 * @version  2.0.0
 * @date     17, Dec, 2014
 *
 * @note
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
 ******************************************************************************/
#include <stdio.h>
#include "Nano100Series.h"	
#include "common.h"
#include "hg-hmd.h"
#include "adv7611.h"
#include "proximity_sensor.h"
#define MODE_2D	0
#define MODE_3D_FULL	1
#define MODE_3D_HALF	2
#define MODE_3D_FRAME	3


#define DATA_FLASH_TEST_BASE        		0xF000
#define DATA_FLASH_CONFIG_NORMAL_BASE		(DATA_FLASH_TEST_BASE + FMC_FLASH_PAGE_SIZE * 1)
#define DATA_FLASH_CONFIG_BACKUP_BASE		(DATA_FLASH_TEST_BASE + FMC_FLASH_PAGE_SIZE * 2)

#define FPGA_PW_EN	PC6
#define FPGA_RESET	PB8
#define ADV_RESET	PA0
#define OLED_RESET	PA3
#define OLED_10V	PC0
#define PA_EN		PB4
struct hg_config tHgConfig;

struct hg_config tHgConfigDefault = {
	2,					// version
	
	// version 1
	{102, 00, 00, 00},	// brightness
	{50, 50, 50, 50},	// contrast
	{30, 50, 50, 50},	// saturation

	// verison 2
	0,					// brightness offset

	0					// reserved
};

#define FPGA_ADDR	(0X10<<1)


uint8_t status_3d=MODE_2D;
uint8_t status_3d_to_set=MODE_2D;
uint8_t is_video_on = 0;

uint32_t oled_config;

uint32_t HgConfigCalcSum(uint32_t *addr, uint32_t count)
{
	uint32_t sum = 0;
	
	while (count--) {
		sum += *(addr++);
	}
	return sum;
}

void HgConfigSetBase(uint32_t base)
{
    uint32_t   au32Config[2];
	uint32_t df_enable_mask = 1;
	uint32_t need_update = 0;

	SYS_UnlockReg();
	FMC_Open();
	
	
    if (FMC_ReadConfig(au32Config, 2) < 0) {
		return;
    }
	
	info("FMC user config[0] : 0x%08X\n", au32Config[0]);
	info("FMC user config[1] : 0x%08X\n", au32Config[1]);
	
	if (au32Config[0] & df_enable_mask) {
		info("data flash is disabled, now enable it\n");
		au32Config[0] &= (~df_enable_mask);
		need_update = 1;
	} else {
		info("data flash is already enabled\n");
	}
	
	if (au32Config[1] == base) {
		info("DFBA(0x%08X) is same as base(0x%08X)\n", au32Config[1], base);
	} else {
		info("DFBA(0x%08X) is different from base(0x%08X), now set it\n", au32Config[1], base);
		au32Config[1] = base;
		need_update = 1;
	}
	
	if (need_update) {
		FMC_ENABLE_CFG_UPDATE();
		if (FMC_WriteConfig(au32Config, 2) < 0) {
			info("write user config failed\n ");
		} else {
			info("write user config ok\n ");
			SYS->IPRST_CTL1 = SYS_IPRST_CTL1_CHIP_RST_Msk;
		}
		FMC_DISABLE_CFG_UPDATE();
	}
}

void HgConfigReadRaw(uint32_t base, struct hg_config *tHgConfig, uint32_t *sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)tHgConfig;
	SYS_UnlockReg();
	FMC_Open();
	for (count = 0; count < (sizeof(struct hg_config) / 4); count++) {
		*(buf++) = FMC_Read(base + count * 4);
	}
	*sum = FMC_Read(base + sizeof(struct hg_config));
	FMC_Close();
	SYS_LockReg();
}

void HgConfigWriteRaw(uint32_t base, struct hg_config *tHgConfig, uint32_t sum)
{
	int count;
	uint32_t *buf;

	buf = (uint32_t *)tHgConfig;
	SYS_UnlockReg();
	FMC_Open();
	FMC_Erase(base);
	for (count = 0; count < (sizeof(struct hg_config) / 4); count++) {
		FMC_Write(base + count * 4, *(buf++));
	}
	FMC_Write(base + sizeof(struct hg_config), sum);
	FMC_Close();
	SYS_LockReg();
}

void HgConfigRead(struct hg_config *tHgConfig)
{
	int count;
	uint32_t *buf;
	uint32_t NormalSumRead;
	uint32_t NormalSumCalc;
	uint32_t BackupSumRead;
	uint32_t BackupSumCalc;
	uint32_t DefaultSumCalc;
	struct hg_config tHgConfigNormal;
	struct hg_config tHgConfigBackup;

	// read normal config
	HgConfigReadRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigNormal, &NormalSumRead);
	NormalSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigNormal, sizeof(struct hg_config) / 4);
	
	// read backup config
	HgConfigReadRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigBackup, &BackupSumRead);
	BackupSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigBackup, sizeof(struct hg_config) / 4);
	
	if (NormalSumCalc == NormalSumRead) {
		// normal config is ok, 
		if (BackupSumCalc == BackupSumRead) {
			// backup config is ok
			// return
			memcpy(tHgConfig, &tHgConfigNormal, sizeof(struct hg_config));
			return;
		} else {
			// backup config is wrong, save backup config from normal config
			HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigNormal, NormalSumCalc);
			// return
			memcpy(tHgConfig, &tHgConfigNormal, sizeof(struct hg_config));
			return;
		}
		
	} else {
		// normal config is wrong, try backup config
		if (BackupSumCalc == BackupSumRead) {
			// backup config is ok, save normal backup from backup config
			HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigBackup, BackupSumCalc);
			// return
			memcpy(tHgConfig, &tHgConfigBackup, sizeof(struct hg_config));
			return;
		} else {
			// backup config is wrong, save normal config and backup config from default config
			DefaultSumCalc = HgConfigCalcSum((uint32_t *)&tHgConfigDefault, sizeof(struct hg_config) / 4);
			HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, &tHgConfigDefault, DefaultSumCalc);
			HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, &tHgConfigDefault, DefaultSumCalc);
			memcpy(tHgConfig, &tHgConfigDefault, sizeof(struct hg_config));
			return;
		}	
	}
}

void HgConfigWrite(struct hg_config *tHgConfig)
{
	uint32_t sum;
	
	sum = HgConfigCalcSum((uint32_t *)tHgConfig, sizeof(struct hg_config) / 4);
	HgConfigWriteRaw(DATA_FLASH_CONFIG_NORMAL_BASE, tHgConfig, sum);
	HgConfigWriteRaw(DATA_FLASH_CONFIG_BACKUP_BASE, tHgConfig, sum);
}

void delays(int s)
{
	while(s--)
		{
			int i=0;
			for(i=0;i<1000;i++)
				CLK_SysTickDelay(1000);	
		}
	


}

//PC1 ----V+
//PC2 ----V-
//PC0 ----BL
//PE5 ---3D


/**
 * @brief       PortA/PortB/PortC IRQ
 *
 * @param       None
 *
 * @return      None
 *
 * @details     The PortA/PortB/PortC default IRQ, declared in startup_nano100series.s.
 */

void GPABC_IRQHandler(void)
{
	
    /* To check if PB.5 interrupt occurred */
    if (PC->ISRC & BIT0) {
        PC->ISRC = BIT0;
        
        //printf("PB.5 INT occurred. \n");

    } 
	else if (PC->ISRC & BIT1) 
      {
      	PC->ISRC = BIT1;
      }
	else if (PC->ISRC & BIT2) 
      {
      	PC->ISRC = BIT2;
      }
	else{
        /* Un-expected interrupt. Just clear all PORTA, PORTB, PORTC interrupts */
        PA->ISRC = PA->ISRC;
        PB->ISRC = PB->ISRC;
        PC->ISRC = PC->ISRC;
       // printf("Un-expected interrupts. \n");
    }
}


/**
 * @brief       PortD/PortE/PortF IRQ
 *
 * @param       None
 *
 * @return      None
 *
 * @details     The PortD/PortE/PortF default IRQ, declared in startup_nano100series.s.
 */
void GPDEF_IRQHandler(void)
{
    /* To check if PE.2 interrupt occurred */
    if (PE->ISRC & BIT5) {
        PE->ISRC = BIT5;

		
		
		
//		if(status_3d==MODE_2D)
//			{
//				Switch_3D(1);
//				status_3d=MODE_3D_FULL;
//			}
//		else
//			{
//				Switch_3D(0);
//				status_3d=MODE_2D;
//			}
		//set_oledconfig(oled_config);
		
	
		
		//set_oledconfig(oled_config);
	
        
       // printf("PE.2 INT occurred. \n");
    } else {
        /* Un-expected interrupt. Just clear all PORTD, PORTE and PORTF interrupts */
        PD->ISRC = PD->ISRC;
        PE->ISRC = PE->ISRC;
        PF->ISRC = PF->ISRC;
        //printf("Un-expected interrupts. \n");
    }
}







/*---------------------------------------------------------------------------------------------------------*/
void SYS_Init(void)
{
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Enable external 12MHz HXT */
    CLK_EnableXtalRC(CLK_PWRCTL_HXT_EN_Msk);
    CLK_EnablePLL(CLK_PLLCTL_PLL_SRC_HXT, 96000000);
    /* Waiting for clock ready */
    CLK_WaitClockReady(CLK_CLKSTATUS_HXT_STB_Msk | CLK_CLKSTATUS_PLL_STB_Msk);

    CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_PLL, CLK_HCLK_CLK_DIVIDER(3));

    /* Select IP clock source */
    CLK_SetModuleClock(USBD_MODULE, 0, CLK_USB_CLK_DIVIDER(2));
	//CLK_SetModuleClock(SPI0_MODULE, CLK_CLKSEL2_SPI0_S_HCLK, 0);
    /* Enable IP clock */
    CLK_EnableModuleClock(USBD_MODULE);
	//CLK_EnableModuleClock(SPI0_MODULE);

    /* Select IP clock source */
    //CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART_S_HXT, CLK_UART_CLK_DIVIDER(1));
   // CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART_S_HXT, CLK_UART_CLK_DIVIDER(1));    
	CLK_SetModuleClock(I2C0_MODULE, 0, 0);
	
	CLK_SetModuleClock(I2C1_MODULE, 0, 0);
    /* Enable IP clock */
    //CLK_EnableModuleClock(UART0_MODULE);
    //CLK_EnableModuleClock(UART1_MODULE);
	CLK_EnableModuleClock(I2C0_MODULE);
	CLK_EnableModuleClock(I2C1_MODULE);
		
    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
    SystemCoreClockUpdate();
		
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Set PA multi-function pins for UART0 RXD and TXD */
   // SYS->PA_H_MFP &= ~( SYS_PA_H_MFP_PA15_MFP_Msk | SYS_PA_H_MFP_PA14_MFP_Msk);
   // SYS->PA_H_MFP |= (SYS_PA_H_MFP_PA15_MFP_UART0_TX|SYS_PA_H_MFP_PA14_MFP_UART0_RX);
    
    /* Set PB multi-function pins for UART1 RXD, TXD  */
    //SYS->PB_L_MFP &= ~(SYS_PB_L_MFP_PB0_MFP_Msk | SYS_PB_L_MFP_PB1_MFP_Msk);
    //SYS->PB_L_MFP |= (SYS_PB_L_MFP_PB0_MFP_UART0_RX | SYS_PB_L_MFP_PB1_MFP_UART0_TX);

	
	/* Set multi function pin for I2C0/I2C1 */
	SYS->PA_H_MFP &= ~(SYS_PA_H_MFP_PA9_MFP_Msk| SYS_PA_H_MFP_PA8_MFP_Msk|SYS_PA_H_MFP_PA10_MFP_Msk| SYS_PA_H_MFP_PA11_MFP_Msk);
	SYS->PA_H_MFP |= (SYS_PA_H_MFP_PA9_MFP_I2C0_SCL | SYS_PA_H_MFP_PA8_MFP_I2C0_SDA|SYS_PA_H_MFP_PA11_MFP_I2C1_SCL | SYS_PA_H_MFP_PA10_MFP_I2C1_SDA  );

	/* Set multi function pin for SPI0 */
	//SYS->PC_L_MFP &= ~(SYS_PC_L_MFP_PC3_MFP_Msk| SYS_PC_L_MFP_PC2_MFP_Msk |SYS_PC_L_MFP_PC1_MFP_Msk | SYS_PC_L_MFP_PC0_MFP_Msk);
    //SYS->PC_L_MFP |= (SYS_PC_L_MFP_PC3_MFP_SPI0_MOSI0| SYS_PC_L_MFP_PC2_MFP_SPI0_MISO0| SYS_PC_L_MFP_PC1_MFP_SPI0_SCLK| SYS_PC_L_MFP_PC0_MFP_SPI0_SS0);
	
   // SYS->PC_L_MFP = (SYS_PC_L_MFP_PC0_MFP_SPI0_SS0 | SYS_PC_L_MFP_PC1_MFP_SPI0_SCLK | SYS_PC_L_MFP_PC2_MFP_SPI0_MISO0 | SYS_PC_L_MFP_PC3_MFP_SPI0_MOSI0);

    /* Set multi function pin for SPI1 */
	//SYS->PB_H_MFP &= ~(SYS_PB_H_MFP_PB9_MFP_Msk);
   // SYS->PB_H_MFP |= (SYS_PB_H_MFP_PB9_MFP_SPI1_SS1);

	SYS_EnableBOD(SYS_BODCTL_BOD25_RST_EN_Msk,SYS_BODCTL_BOD25_EN_Msk);
                      

    /* Lock protected registers */
    SYS_LockReg();
}

//PC1 ----V+
//PC2 ----V-
//PC0 ----BL
//PE5 ---3D

void key_init()
{

	/* Configure PB5 as Input mode and enable interrupt by rising edge trigger */
    GPIO_SetMode(PA, BIT2, GPIO_PMD_INPUT);
    
	GPIO_SetMode(PA, BIT14, GPIO_PMD_INPUT);
    

	GPIO_SetMode(PA, BIT15, GPIO_PMD_INPUT);
    

    

   // NVIC_EnableIRQ(GPDEF_IRQn);
}


void save_config(uint32_t value)
{
	SYS_UnlockReg();
	//HDMI_init();
	FMC_Open();
	
	FMC_Erase(DATA_FLASH_TEST_BASE);
	FMC_Write(DATA_FLASH_TEST_BASE, value);
	
	FMC_Close();
	SYS_LockReg();

}

uint32_t read_config()
{
	uint32_t value;
	SYS_UnlockReg();
	//HDMI_init();
	FMC_Open();	
	value=FMC_Read(DATA_FLASH_TEST_BASE);
	
	FMC_Close();
	SYS_LockReg();
	return value;
}



/*---------------------------------------------------------------------------------------------------------*/
/*  Main Function                                                                                          */
/*---------------------------------------------------------------------------------------------------------*/
int32_t main (void)
{
	
	static uint8_t spi_buf[10],isAddValue=TRUE,read_dat;
	int key_bright_count = 0;
	int count_delay=0;
	int bright_delta = 12;
	enum proximity_sensor_state state;
	
    SYS_Init();
	//UART_Open(UART0, 115200);
	//info("\nHighglass HMD power on ...\n");
	
	
	HgConfigSetBase(DATA_FLASH_TEST_BASE);
	HgConfigRead(&tHgConfig);
	//info("Read config :\n");
	//HgConfigDump();

    USBD_Open(&gsInfo, HMD_ClassRequest, NULL);

    /* Endpoint configuration */
    HMD_Init();
    NVIC_EnableIRQ(USBD_IRQn);
		//NVIC_EnableIRQ(UART0_IRQn);
		//NVIC_EnableIRQ(UART1_IRQn);

//	GPIO_SetMode(PB, BIT5, GPIO_PMD_OUTPUT);
//	GPIO_SetMode(PB, BIT4, GPIO_PMD_OUTPUT);
	GPIO_SetMode(PA, BIT13, GPIO_PMD_OUTPUT);
	GPIO_SetMode(PA, BIT12, GPIO_PMD_INPUT);
	GPIO_SetMode(PC, BIT6, GPIO_PMD_OUTPUT);
	GPIO_SetMode(PA, BIT0, GPIO_PMD_OUTPUT);
	GPIO_SetMode(PA, BIT3, GPIO_PMD_OUTPUT);
	GPIO_SetMode(PB, BIT8, GPIO_PMD_OUTPUT);//fpga reset

	GPIO_SetMode(PB, BIT4, GPIO_PMD_OUTPUT);//
	GPIO_SetMode(PC, BIT0, GPIO_PMD_OUTPUT);//


//	GPIO_SetMode(PB, BIT12, GPIO_PMD_OPEN_DRAIN);//
//	GPIO_SetMode(PB, BIT9, GPIO_PMD_OPEN_DRAIN);//
//	GPIO_SetMode(PA, BIT15, GPIO_PMD_OPEN_DRAIN);//
//	GPIO_SetMode(PA, BIT14, GPIO_PMD_OPEN_DRAIN);//
//	GPIO_SetMode(PA, BIT2, GPIO_PMD_OPEN_DRAIN);//
	
	sensor_init();
	proximity_sensor_init();
	

	ADV_RESET=0;
	OLED_RESET=0;
	
	FPGA_RESET=0;
	CLK_SysTickDelay(300000);	
	FPGA_RESET=1;
	
	ADV_RESET=1;

	FPGA_PW_EN=1;
	OLED_RESET=1;
	
	OLED_10V=1;
	PA_EN=1;
	
	i2c_init();
	
	oled_config=0x5a050302;	
	
	SPI_init();
	
	
	//HDMI_init();
	//while(1);
//	oled_config=read_config();
//	if((oled_config&0xff000000)!=0x5a000000)
//		{
//			oled_config=0x5a050302;
//			save_config(oled_config);
//		}
//	
//	set_oledconfig(oled_config);

	//lum_config_l=20;
	
	adv7611_device_init();
	//key_init();
	OLED_10V=0;
	oled_init();
	//delays(5);
	oled_start();
	//while(1);
	USBD_Start();
	
	
//	read_dat=read_reg_8(FPGA_ADDR,3);
	

	
	while(1)
		{
			HMD_TransferData();
			
			if(count_delay++>20)
				{
					state = proximity_sensor_get_state();
					if (state == PROXIMITY_SENSOR_STATE_CLOSE) {
						//info("PROXIMITY_SENSOR_STATE_CLOSE\n");
						//info("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN\n");
						count_delay=0;
						if (is_video_on == 0) {
							//hdmi_video_start();
							//CLK_SysTickDelay(10000);
							oled_start();
							is_video_on = 1;
						}
					} else {
						
						//info("PROXIMITY_SENSOR_STATE_LEAVE\n");
						//info("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n");
						if (is_video_on &&count_delay>1500) {
							is_video_on = 0;
							oled_stop();
							//hdmi_video_stop();
							
						}
					}

				}
				
			read_dat = adv7611_read(IO___MAP_I2C,0x6a); 
			if(read_dat&0x04)
				{
					status_3d_to_set=MODE_3D_FRAME;
					//if(!(0x20==read_reg_8(FPGA_ADDR,3)))
					//	write_reg_8(FPGA_ADDR,3,0x20);
					
				}
			else
				{
					if(status_3d==MODE_3D_FRAME)
						status_3d_to_set=MODE_2D;
					//if(0x20==read_reg_8(FPGA_ADDR,3))						
						//write_reg_8(FPGA_ADDR,3,0x00);
				}
			if (is_video_on) {
				// 3d mode cmd check
				if (status_3d != status_3d_to_set) {
					if (status_3d_to_set == MODE_2D) {
						write_reg_8(FPGA_ADDR,3,0x00);
						status_3d=MODE_2D;
					} else if (status_3d_to_set == MODE_3D_FULL) {
						//Switch_3D(1);
						write_reg_8(FPGA_ADDR,3,0x11);
						status_3d=MODE_3D_FULL;
					}else if (status_3d_to_set == MODE_3D_FRAME) {
						//Switch_3D(1);
						FPGA_RESET=0;
						CLK_SysTickDelay(300000);	
						FPGA_RESET=1;
						CLK_SysTickDelay(300000);	
						write_reg_8(FPGA_ADDR,3,0x20);
						status_3d=MODE_3D_FRAME;

						
					}
				}
			}
				
			if(PA12==0)
				{
					if(status_3d==MODE_2D)
						{
							//Switch_3D(1);
							write_reg_8(FPGA_ADDR,3,0x11);
							status_3d=MODE_3D_FULL;
							status_3d_to_set = MODE_3D_FULL;
						}	
					else if(status_3d==MODE_3D_FRAME)
						{
							read_dat=read_reg_8(FPGA_ADDR,4);
							if(read_dat==0)
								write_reg_8(FPGA_ADDR,4,0xff);//switch left and right
							else
								write_reg_8(FPGA_ADDR,4,0x00);
						}
					else
						{
							//Switch_3D(0);
							write_reg_8(FPGA_ADDR,3,0x00);
							status_3d=MODE_2D;
							status_3d_to_set = MODE_2D;
						}
					while(PA12==0);
					//set_oledconfig(oled_config);
				}

			if(PA14==0)//+
				{

					if((oled_config&0xff)<=5)						
						{
							oled_config++;
							//set_oledconfig(oled_config);
							//save_config(oled_config);
						}
					
						
					//LumChange(TRUE);
					while(PA14==0);
				}

			
					// brightness key check
				if (PA13 == 0) {
					// debounce
					CLK_SysTickDelay(1000);
					if (PA13 == 0) {
						if (key_bright_count >= 10) {
							tHgConfig.tBrightness.w += bright_delta;
							if (tHgConfig.tBrightness.w >= BRIGHTNESS_MAX) {
								tHgConfig.tBrightness.w = BRIGHTNESS_MAX;
								bright_delta = -12;
							} else if (tHgConfig.tBrightness.w <= BRIGHTNESS_MIN) {
								tHgConfig.tBrightness.w = BRIGHTNESS_MIN;
								bright_delta = 12;
							}
							SetBrightness(&tHgConfig.tBrightness);
							HgConfigWrite(&tHgConfig);
							
							key_bright_count = -10000000;
							info("on key bright, tHgConfig.tBrightness.w : %u\n",
								tHgConfig.tBrightness.w);
						} else {
							key_bright_count++;
						}
					}
				} else {
					key_bright_count = 0;
				}
			

				

			
			//CLK_SysTickDelay(3000);
			//hdmi_monitor(status_3d);

			HMD_TransferData();
		}
    
}



/*** (C) COPYRIGHT 2013 Nuvoton Technology Corp. ***/

